/**
 * 
 */
package com.couchcrowler.server.parser;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import com.couchcrowler.shared.Profile;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;


/**
 * @author vienozin
 * 
 */
public class ProfileParser {
	
	private String htmlTransformerUrl = "http://html-transformer.appspot.com/transform";
	
	public Profile parseId(String id) {
		Profile result = parse(getUrlForId(id));
		
		if (result.getId() == null) {
			/*
			 * Private profile
			 */
			result.setId(id);
			result.setPrivateProfile(true);
		}
		
		return result;
	}
	
	private URL getUrlForId(String id) {
		try {
			return new URL(htmlTransformerUrl + "?sourceBase64=" + new String(Base64.encodeBase64(("http://www.couchsurfing.org/profile.html?id=" + id + "&all_friends=1").getBytes())));
		} catch (MalformedURLException e) {
			throw new Error(e);
		}
	}
	
	public Set<Profile> parseIds(Set<String> ids) {
		URLFetchService fetchService = URLFetchServiceFactory.getURLFetchService();
		List<Future<HTTPResponse>> futures = new ArrayList<Future<HTTPResponse>>();
		for (String id : ids) {
			Future<HTTPResponse> future = fetchService.fetchAsync(getUrlForId(id));
			futures.add(future);			
		}

		Set<Profile> result = new HashSet<Profile>();
		boolean allCompleated = false;
		
		while (!allCompleated) {
			allCompleated = true;
			
			for (Future<HTTPResponse> future : futures) {
				if (!future.isCancelled()) {
					try {
						HTTPResponse response = future.get(1, TimeUnit.SECONDS);
						Profile profile = parseProfile(new String(response.getContent()));
						if (profile != null) {
							result.add(profile);
						}
						
						future.cancel(true);
					} catch (InterruptedException e) {
						future.cancel(true);
					} catch (ExecutionException e) {
						future.cancel(true);
					} catch (TimeoutException e) {
						allCompleated = false;
					}
				}
			}
		}
		
		return result;
	}
	
	private Profile parse(URL url) {
		Profile result = null;
		
		try {
			URLConnection connection = url.openConnection();
			connection.setConnectTimeout(10000);
			connection.setReadTimeout(30000);
			
			result = parseProfile(IOUtils.toString(connection.getInputStream()));
		} catch (IOException e) {
			throw new Error(e);
		}
		
		return result;
	}

	private Profile parseProfile(String profile) {
		Profile result = null;
		
		try {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			factory.setNamespaceAware(true);
	
			SAXParser parser = factory.newSAXParser();
			XMLReader reader = parser.getXMLReader();
			
			ProfileHandler handler = new ProfileHandler();
			reader.setContentHandler(handler);
			reader.parse(new InputSource(IOUtils.toInputStream(profile)));
	
			result = handler.getProfile();
		} catch (SAXException e) {
			throw new Error(e);
		} catch (ParserConfigurationException e) {
			throw new Error(e);
		} catch (IOException e) {
			throw new Error(e);
		}
		
		return result;
	}
}
